package com.test.cidr;

import java.util.HashMap;
import java.util.Map;

/**
 * @author zhongrunfu
 * 日期：2014-10-28
 * 描述：根据某个IP，查找相应的CIDR IP地块，以及目标主机地址
 *
 */
public class CIDRDemo {
    /**
     * 最大IP的256进制LONG值的KEY
     */
    public static final String CIDR_MAX_IP = "CIDR_MAX_IP";
    /**
     * 最小IP的256进制LONG值的KEY
     */
    public static final String CIDR_MIN_IP = "CIDR_MIN_IP";
    /*
     * cidrIp与目标IP映射表
     * 例如：configIp = 172.38.64.0/22,172.28.72.11
     * key=172.38.64.0/22
     * value=172.28.72.11
     */
    private Map cidrIpToTargetIp;
    /*
     * cidrIp表示的范围
     * 用256进制值表示最大最小值
     * 其中包括非法IP（主机ID全为0或1的IP）
     * 例如：cidrIp = 172.38.64.0/22
     * value = Map<MAX_VALUE,LONG>,MAP<MIN_VALUE,LONG>
     */
    private Map cidrIpBlock;
    /*
     * CIDR IP 数组
     * 例如：[172.28.64.0/22,172.16.0.0/12]
     */
    private String[] cidrIps;

    /**
     * 构造方法
     *
     * @param bindIpConfig 配置项
     *                     例如：172.28.64.0/22,172.28.72.11;172.16.0.0/12,172.28.80.27
     */
    public CIDRDemo(String bindIpConfig) {
        this.init(bindIpConfig);//初始化
    }

    /**
     * 获取目标IP
     * 如果CIDR格式的IP段有重复，则返回首个地址
     *
     * @param ip 给定的IP地址
     * @return
     */
    public String rout(String ip) {
        if (ip == null || "".equals(ip.trim())) {
            return null;
        }
        long ipValue = this.ipToNumeric(ip);//IP转换为256进制值
        String cidrIp = "";
        for (int i = 0, len = this.cidrIps.length; i < len; i++) {
            cidrIp = this.cidrIps[i];
            if (this.isInBlock(ipValue, (Map) this.cidrIpBlock.get(cidrIp))) {
                return (String) this.cidrIpToTargetIp.get(cidrIp);
            }
        }
        return null;
    }

    /*
     * 判断是否在给定的范围内
     *
     * @param ipValue 判断的IP
     * @param blockMap IP值范围
     * @return 如果是返回true,否则返回false
     */
    private boolean isInBlock(long ipValue, Map blockMap) {
        Long maxIpValue = (Long) blockMap.get(CIDRDemo.CIDR_MAX_IP);
        Long minIpValue = (Long) blockMap.get(CIDRDemo.CIDR_MIN_IP);
        return (ipValue >= minIpValue.longValue()) && (ipValue <= maxIpValue.longValue());
    }

    /*
     * 初始化
     * @param bindIpConfig 格式172.28.64.0/22,172.28.72.11;172.16.0.0/12,172.28.80.27
     */
    private void init(String bindIpConfig) {
        this.print("初始化开始--------->");
        long time = System.currentTimeMillis();
        if (bindIpConfig == null || "".equals(bindIpConfig.trim())) {
            throw new RuntimeException("参数不能为空");
        }
        this.print("");
        String[] bindIps = bindIpConfig.split(";");
        if (bindIps == null || bindIps.length == 0) {
            throw new RuntimeException("参数格式不正确，必须像以下格式x.x.x.x/n,a.a.a.a;x.x.x.x/n;b.b.b.b");
        }
        int len = bindIps.length;
        this.cidrIps = new String[len];//----
        this.cidrIpBlock = new HashMap();//----
        this.cidrIpToTargetIp = new HashMap();//----
        String[] cidrIpTargetIp = null;
        for (int i = 0; i < len; i++) {
            this.print("02--开始按,号分割[" + bindIps[i] + "]");
            cidrIpTargetIp = bindIps[i].split(",");//分割CIDR与目标IP地址
            if (cidrIpTargetIp == null || cidrIpTargetIp.length != 2) {
                throw new RuntimeException("参数格式不正确,CIDR地址与目标IP地址需要逗号分格,例如：x.x.x.x/n,a.a.a.a");
            }
            cidrIps[i] = cidrIpTargetIp[0];//CIDR地址
            this.cidrIpToTargetIp.put(cidrIpTargetIp[0], cidrIpTargetIp[1]);//-->map<cidr,targetIp>
            this.initCidrIpBlock(cidrIpTargetIp[0]);
        }
        this.print("初始化完成---->共花费时间（毫秒）:" + (System.currentTimeMillis() - time));
    }

    /*
     * 初始化CIDR IP范围
     * @param cidrIp 例如：x.x.x.x/n
     */
    private void initCidrIpBlock(String cidrIp) {
        this.print("初始化CIDR IP范围，CIDR IP=[" + cidrIp + "]");
        if (cidrIp == null || "".equals(cidrIp.trim())) {
            throw new RuntimeException("[" + cidrIp + "]参数格式不正确，CIDR地址部分为空");
        }
        String[] ipIds = cidrIp.split("\\/");
        if (ipIds == null || ipIds.length != 2) {
            throw new RuntimeException("[" + cidrIp + "]参数格式不正确，CIDR地址格式不正确，正确格式为x.x.x.x/n");
        }
        int num = Integer.parseInt(ipIds[1]);
        if (num > 32 || num < 4) {
            throw new RuntimeException("[" + cidrIp + "]参数格式不正确，CIDR地址格式不正确，网络ID值必须在（4,32）范围内");
        }
        //TODO
        String networkId = this.getNetworkId(cidrIp);
        String maxIpAddres = this.getMaxIpAddres(networkId, this.getMaskRevert(num));
        Map map = new HashMap();
        map.put(CIDRDemo.CIDR_MAX_IP, Long.valueOf(this.ipToNumeric(maxIpAddres)));
        map.put(CIDRDemo.CIDR_MIN_IP, Long.valueOf(this.ipToNumeric(networkId)));
        this.cidrIpBlock.put(cidrIp, map);//cidr ip范围
        this.print("[" + cidrIp + "]IP 256进制值范围为[" + map.get(CIDRDemo.CIDR_MIN_IP) + "," + map.get(CIDRDemo.CIDR_MAX_IP) + "]");
        this.print("[" + cidrIp + "]IP 范围为[" + networkId + "," + maxIpAddres + "]");
    }

    /*
     * 获取网络ID，即也是CIDR表示的最小IP
     * @param ipCidr CIDR法表示的IP，例如：172.16.0.0/12
     * @return 网络ID，即也是CIDR表示的最小IP
     */
    private String getNetworkId(String ipCidr) {
        String[] ipMaskLen = ipCidr.split("\\/");
        String mask = this.getMask(Integer.parseInt(ipMaskLen[1]));
        String[] ips = ipMaskLen[0].split("\\.");
        String[] masks = mask.split("\\.");
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 4; i++) {
            sb.append(Integer.parseInt(ips[i]) & Integer.parseInt(masks[i]));
            if (i != 3) {
                sb.append(".");
            }
        }
        return sb.toString();
    }

    /*
     * 获取掩码
     * @param maskLength 网络ID位数
     * @return
     */
    private String getMask(int maskLength) {
        int binaryMask = 0xFFFFFFFF << (32 - maskLength);
        StringBuffer sb = new StringBuffer();
        for (int shift = 24; shift > 0; shift -= 8) {
            sb.append(Integer.toString((binaryMask >>> shift) & 0xFF));
            sb.append(".");
        }
        sb.append(Integer.toString(binaryMask & 0xFF));
        return sb.toString();
    }

    /*
     * 获取IP最大值
     * @param netId 网络ID
     * @param maskReverse 掩码反码
     * @return
     */
    private String getMaxIpAddres(String netId, String maskReverse) {
        String[] netIdArray = netId.split("\\.");
        String[] maskRevertArray = maskReverse.split("\\.");
        StringBuffer sb = new StringBuffer();
        for (int i = 0, len = netIdArray.length; i < len; i++) {
            sb.append(Integer.parseInt(netIdArray[i]) + Integer.parseInt(maskRevertArray[i]));
            if (i != len - 1) {
                sb.append(".");
            }
        }
        return sb.toString();
    }

    /*
     * 获取掩码整型数组
     * @param maskLength 网络ID位数
     * @return
     */
    private int[] getmaskArray(int maskLength) {
        int binaryMask = 0xFFFFFFFF << (32 - maskLength);
        int[] mask = new int[4];
        for (int shift = 24, k = 0; shift > 0; shift -= 8) {
            mask[k] = (binaryMask >>> shift) & 0xFF;
            k++;
        }
        mask[3] = binaryMask & 0xFF;
        return mask;
    }

    /*
     * 获取掩码的反码
     * @param maskLength 网络ID位数
     * @return
     */
    private String getMaskRevert(int maskLength) {
        int binaryMask = 0xFFFFFFFF << (32 - maskLength);
        binaryMask = binaryMask ^ 0xFFFFFFFF;
        StringBuffer sb = new StringBuffer(15);
        for (int shift = 24; shift > 0; shift -= 8) {
            sb.append(Integer.toString((binaryMask >>> shift) & 0xFF));
            sb.append(".");
        }
        sb.append(Integer.toString(binaryMask & 0xFF));
        return sb.toString();
    }

    /*
     * IP地址转换为一个256进制的long整数
     * @param ip
     * @return
     */
    private long ipToNumeric(String ip) {
        String[] ips = ip.split("\\.");
        Long[] ipLong = new Long[4];
        for (int i = 0, len = ips.length; i < len; i++) {
            ipLong[i] = Long.parseLong(ips[i]);
        }
        long result = ipLong[3] & 0xFF;
        result |= ((ipLong[2] << 8)) & 0xFF00;
        result |= ((ipLong[1] << 16)) & 0xFF0000;
        result |= ((ipLong[0] << 24)) & 0xFF000000;
        return result;
    }

    /*
     * 十进制转二进制
     * @param n
     * @return
     */
    private String toBinary(int n) {
        return Integer.toBinaryString(n);
    }

    /*
     * 二进制转十进制
     * @param bit
     * @return
     */
    private int binaryToTen(String bit) {
        return Integer.valueOf(bit, 2);
    }

    private void print(Object obj) {
        System.out.println(obj);
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String ipConfig = "172.28.64.0/22,172.28.72.11;172.16.0.0/12,172.28.80.27";
        String ip = "172.28.68.0";
        CIDRDemo router = new CIDRDemo(ipConfig);
        long time = System.currentTimeMillis();
        router.print("请求地址：" + ip);
        router.print("目标地址：" + router.rout(ip));
        router.print("共花费时间（毫秒）：" + (System.currentTimeMillis() - time));

        router.print("-----------------------------");
        String ipCidr = "172.28.64.0/22";
        int num = 22;
        router.print("CIDR IP:[" + ipCidr + "]");

        String mask = router.getMask(num);
        router.print("掩码:[" + mask + "]");

        int[] getmaskArray = router.getmaskArray(num);
        router.print("掩码整型数组:[" + getmaskArray + "]");

        String networkId = router.getNetworkId(ipCidr);
        router.print("网络ID（最小IP）:[" + networkId + "]");

        String maskRevert = router.getMaskRevert(num);
        router.print("掩码:[" + mask + "]" + "的反码:[" + maskRevert + "]");

        String maxIpAddres = router.getMaxIpAddres(networkId, maskRevert);
        router.print("最大IP地址:[" + maxIpAddres + "]");

        long minIpValue = router.ipToNumeric(networkId);
        long maxIpValue = router.ipToNumeric(maxIpAddres);
        router.print("[" + ipCidr + "]IP 256进制值范围为[" + minIpValue + "," + maxIpValue + "]");
    }

}
